import pickle
import socket
import sys
import threading
from datetime import datetime

class DUtils:
    class base_flag:
        def make(func):
            if func not in locals().values():
                raise ValueError(f"{func} is not in locals().values(): {locals().values()}")
            return bytes([func])
    class flags(base_flag):
        signin = bytes([0x00])
        signup = bytes([0x01])
        message = bytes([0x02])
        group  = bytes([0x03])
        logout = bytes([0xff])
    
    class f_login(base_flag):
        success = bytes([0x01])
        failed  = bytes([0x00])

    class f_register(base_flag):
        success = bytes([0x01])
        failed  = bytes([0x00])
    
    class f_group(base_flag):
        setup = bytes([0x00])
        join = bytes([0x01])
        quit = bytes([0x02])

    class f_message(base_flag):
        private = bytes([0x00])
        public = bytes([0x01])

    def parse(data):
        ptr = memoryview(data)
        flag = ptr[0].to_bytes(1, "big")
        if flag == DUtils.flags.message:
            return 0x02, DUtils.parse_message(data)
        elif flag == DUtils.flags.signin:
            return 0x00, DUtils.parse_login_s(data)
        elif flag == DUtils.flags.signup:
            return 0x01, DUtils.parse_register_s(data)
        elif flag == DUtils.flags.logout:
            return 0xff, ptr[1:].tobytes()
        else:
            return 0xfff, None

    def join(*args):
        r = bytearray()
        for i in args:
            r.extend(i)
        return bytes(r)

    def parse_login_s(data):
        ptr = memoryview(data)
        if ptr[0].to_bytes(1, "big") != DUtils.flags.signin:
            return
        uid = int.from_bytes(ptr[1:5].tobytes(), "big")
        pwd = ptr[5:].tobytes().decode('utf-8')
        return uid, pwd
    
    def parse_login_c(data):
        ptr = memoryview(data)
        if ptr[:2].tobytes() == DUtils.flags.signin + DUtils.f_login.failed:
            return ptr[6:].tobytes().decode('utf-8')
        return pickle.loads(ptr[2:])
    
    def login_success(package:bytes):
        msg = DUtils.flags.signin + DUtils.f_login.success + package
        return msg.__len__().to_bytes(2, 'big') + msg
    
    def login_failed(uid:int, info:str):
        msg = DUtils.flags.signin + DUtils.f_register.failed + uid.to_bytes(4, 'big') + info.encode('utf-8')
        return msg.__len__().to_bytes(2, 'big') + msg
    
    def login_session(uid:int, pwd:str):
        msg = DUtils.flags.signin + uid.to_bytes(4) + pwd.encode('utf-8')
        return msg.__len__().to_bytes(2, 'big') + msg
    
    def logout_session(uid:int):
        msg = DUtils.flags.logout + uid.to_bytes(4)
        return msg.__len__().to_bytes(2, 'big') + msg
    
    def parse_register_s(data):
        ptr = memoryview(data)
        if ptr[0].to_bytes(1, "big") != DUtils.flags.signup:
            return
        uid = int.from_bytes(ptr[1:5].tobytes(), "big")
        pwd = ptr[5:].tobytes().decode('utf-8')
        return uid, pwd
    
    def parse_register_c(data):
        ptr = memoryview(data)
        if ptr[:2].tobytes() == DUtils.flags.signup + DUtils.f_register.failed:
            return ptr[6:].tobytes().decode('utf-8')
        return pickle.loads(ptr[2:])
    
    def register_session(uid:int, pwd:str):
        msg = DUtils.flags.signup + uid.to_bytes(4) + pwd.encode('utf-8')
        return msg.__len__().to_bytes(2, 'big') + msg
    
    def register_success(package:bytes):
        msg = DUtils.flags.signup + DUtils.f_register.success + package
        return msg.__len__().to_bytes(2, 'big') + msg
    
    def register_failed(uid:int, info:str):
        msg = DUtils.flags.signup + DUtils.f_register.failed + uid.to_bytes(4, 'big') + info.encode('utf-8')
        return msg.__len__().to_bytes(2, 'big') + msg
    
    def message_session(text:str, func:int, gid:int=0, uid:int=0):
        if func == 0x0:
            msg = DUtils.flags.message + DUtils.f_message.private + uid.to_bytes(4, 'big') + text.encode('utf-8')
        elif func == 0x1:
            msg = DUtils.flags.message + DUtils.f_message.public + gid.to_bytes(4, 'big') + text.encode('utf-8')
        return msg.__len__().to_bytes(2, 'big') + msg

    def parse_message(data):
        if data == b'':return
        ptr = memoryview(data)
        if ptr[0].to_bytes(1, "big") != DUtils.flags.message:
            return
        func = ptr[1]
        ug_id = int.from_bytes(ptr[2:6].tobytes(), 'big')
        text = ptr[6:].tobytes().decode('utf-8')
        return func, ug_id, text
    
    def group_session(gid:int, func:int):
        msg = DUtils.flags.group + DUtils.f_group.make(func) + gid.to_bytes(4, 'big')
        return msg.__len__().to_bytes(2, 'big') + msg

class Logger:
    def __init__(self, name, thread_name) -> None:
        self.name = name
        self.thread_name = thread_name
        self.format = "{time} [{t_name}] [{level}] {msg}"
        self.print = print
    
    def new(self, thread_name):
        return Logger(name=self.name, thread_name=thread_name)

    def log(self, *msg, level="LOG"):
        b = []
        for i in msg:
            b.append(str(i))
        print(self.format.format(time=datetime.today().isoformat(), t_name=self.thread_name, level=level, msg=" ".join(b)))
    
    def debug(self, *msg):
        self.log(level="DEBUG", *msg)

    def info(self, *msg):
        self.log(level="INFO", *msg)

    def warning(self, *msg):
        self.log(level="WARNING", *msg)

    def error(self, *msg):
        self.log(level="ERROR", *msg)

    def fatal(self, *msg):
        self.log(level="FATAL", *msg)